//@author A0096509U
#include "TaskItStorage.h"

TaskItStorage::TaskItStorage() {
	Logger *_logFile;
	_logFile = Logger::getInstance();
	_numOfTask = INITIAL_VALUE;
	_logFile->loggingEvents(LOG_STORAGE_INITIALIZED);
	_myFileName = STORAGE_FILE_NAME;

	while (!_allPreviousActions.empty()) {
		_allPreviousActions.pop();
	}
}

void TaskItStorage::clearTasks() {
	Date today, currentDate;
	today.setLocalDate();
	for (int i = _numOfTask - 1; i > INVALID_INDEX; i--) {
		currentDate = _listOfTasks[i].getDate();
		if (today.isLaterDate(currentDate) && _listOfTasks[i].getDone()) {
			_listOfTasks.erase(_listOfTasks.begin() + i);
		}
	}
	_numOfTask = _listOfTasks.size();
	sortStorage();
	saveFile();
	while (!_allPreviousActions.empty()) {
		_allPreviousActions.pop();
	}
}

void TaskItStorage::sortStorage() {
	Logger *_logFile;
	_logFile = Logger::getInstance();
	bool sorted = false;
	int i = INITIAL_VALUE;
	Task temp;

	while(!sorted) {
		sorted = true;
		i++;
		for (int j = INITIAL_VALUE; j < _numOfTask - i; j++) {
			if (_listOfTasks[j].getDate().isLaterDate((_listOfTasks[j + SHIFT_INDEX_BY_ONE].getDate()))) {
				temp = _listOfTasks[j];
				_listOfTasks[j] = _listOfTasks[j + SHIFT_INDEX_BY_ONE];
				_listOfTasks[j + SHIFT_INDEX_BY_ONE] = temp;
				sorted = false;
			}
		}
	}

	sorted = false;
	i = INITIAL_VALUE;
	while(!sorted) {
		sorted = true;
		for (int i = INITIAL_VALUE; i < _numOfTask - SHIFT_INDEX_BY_ONE; i++) {
			if (_listOfTasks[i].getDate().isSameDate(_listOfTasks[i + SHIFT_INDEX_BY_ONE].getDate()) && 
				_listOfTasks[i].getTimeStart().isLaterTime((_listOfTasks[i + SHIFT_INDEX_BY_ONE].getTimeStart()))) {
				temp = _listOfTasks[i];
				_listOfTasks[i] = _listOfTasks[i + SHIFT_INDEX_BY_ONE];
				_listOfTasks[i + SHIFT_INDEX_BY_ONE] = temp;
				sorted = false;
			}
		}
	}
	_logFile->loggingEvents(LOG_STORAGE_SORT);
}

void TaskItStorage::readFile() {
	Logger *_logFile;
	_logFile = Logger::getInstance();
	ifstream myFile(_myFileName);

	if(myFile.good()) {
		string activity, venue, category, timeStartString, timeEndString, taskDateString, readDone;
		int taskDate;
		
		while(getline(myFile,activity)) {
			getline(myFile,venue);
			getline(myFile,timeStartString);
			getline(myFile,timeEndString);
			getline(myFile,category);
			getline(myFile,taskDateString);
			Date date;
			if(taskDateString != EMPTY_FIELD) {
				taskDate = atoi(taskDateString.c_str());
				date.setDate(taskDate);
			}
			TaskItTime timeStart;
			TaskItTime timeEnd;
			if(timeStartString != EMPTY_FIELD) {
				timeStart.setTime(timeStartString);
			}
			if(timeEndString != EMPTY_FIELD) {
				timeEnd.setTime(timeEndString);
			}
			getline(myFile,readDone);
			Task task(activity, venue, timeStart, timeEnd, category, date);
			if (readDone == READ_DONE.c_str()) {
				task.markDone();
				}

			_listOfTasks.push_back(task);
			_numOfTask++;
			_logFile->loggingEvents(LOG_STORAGE_FILE_OPENED);
		}
		sortStorage();
	} else {
		ofstream newFile;
		newFile.open(_myFileName);
		newFile.close();
		_logFile->loggingEvents(LOG_STORAGE_CREATE_NEW_FILE);
	}
	return;
}

void TaskItStorage::saveFile() {
	Logger *_logFile;
	_logFile = Logger::getInstance();
	ofstream myFile;
	myFile.open(_myFileName, ios::trunc);

	for(int i = INITIAL_VALUE; i < getNumOfTask(); i++) {
		myFile << _listOfTasks[i].getActivity() << endl;
		myFile << _listOfTasks[i].getVenue() << endl;
		myFile << _listOfTasks[i].getTimeStart().getTimeStorage() << endl;
		myFile << _listOfTasks[i].getTimeEnd().getTimeStorage() << endl;
		myFile << _listOfTasks[i].getCategory() << endl;
		myFile << _listOfTasks[i].getDate().getDisplay() << endl;
		if (_listOfTasks[i].getDone()) {
			myFile << READ_DONE.c_str() << endl;
		} else {
			myFile << READ_NOT_DONE.c_str() << endl;
		}
	}

	myFile.close();
	_logFile->loggingEvents(LOG_STORAGE_SAVED);
	return;
}

void TaskItStorage::addTempTask(string activity, string venue, TaskItTime timeStart, TaskItTime timeEnd, string category, Date date) {
	Logger *_logFile;
	_logFile = Logger::getInstance();
	ofstream tempFile;
	tempFile.open(TEMP_NAME,ios::trunc);
	
	tempFile << activity << endl;
	tempFile << venue << endl;
	tempFile << timeStart.getTimeStorage() << endl;
	tempFile << timeEnd.getTimeStorage() << endl;
	tempFile << category << endl;
	tempFile << date.getDisplay() << endl;
	tempFile << READ_NOT_DONE.c_str() << endl;
	_logFile->loggingEvents(LOG_STORAGE_TEMPFILE);
	tempFile.close();
}

void TaskItStorage::addConflictedTask(int &index) {
	Logger *_logFile;
	_logFile = Logger::getInstance();
	ifstream myFile(TEMP_NAME);
	string activity, venue = EMPTY_STRING, category, taskTimeStartString, taskTimeEndString, taskDateString, readDone;
	int taskDate;
		
	if(getline(myFile,activity)) {
		getline(myFile,venue);
		getline(myFile,taskTimeStartString);
		getline(myFile,taskTimeEndString);
		getline(myFile,category);
		getline(myFile,taskDateString);
		taskDate = atoi(taskDateString.c_str());
		Date date;
		TaskItTime timeStart;
		TaskItTime timeEnd;
		date.setDate(taskDate);
		if(taskTimeStartString != EMPTY_FIELD) {
			timeStart.setTime(taskTimeStartString);
		}
		if(taskTimeEndString != EMPTY_FIELD) {
			timeEnd.setTime(taskTimeEndString);
		}
		getline(myFile,readDone);
		index = addTask(activity, venue, timeStart, timeEnd, category, date);
	}

	ofstream tempFile;
	tempFile.open(TEMP_NAME,ios::trunc);
	tempFile.close();
	_logFile->loggingEvents(LOG_STORAGE_ADD_CONFLICTED);

	if(venue == EMPTY_STRING) {
		sprintf_s(buffer, MESSAGE_INVALID.c_str());
		throw showToUser(buffer);
	}
}

void TaskItStorage::doNotAddConflictedTask() {
	Logger *_logFile;
	_logFile = Logger::getInstance();
	ifstream myFile(TEMP_NAME);
	string activity, venue = EMPTY_STRING;
		
	if(getline(myFile,activity)) {
		getline(myFile,venue);
	}
	
	ofstream tempFile;
	tempFile.open(TEMP_NAME,ios::trunc);
	tempFile.close();
	_logFile->loggingEvents(LOG_STORAGE_NOT_ADD_CONFLICTED);

	if(venue == EMPTY_STRING) {
		sprintf_s(buffer, MESSAGE_INVALID.c_str());
		throw showToUser(buffer);
	}
}

int TaskItStorage::getTaskIndex(Task task) {
	for (int i = INITIAL_VALUE; i < getNumOfTask(); i++) {
		if (_listOfTasks[i].isSameTask(task)) {
			return i;
		}
	}
	return INVALID_INDEX;
}

int TaskItStorage::addTask(string activity, string venue, TaskItTime timeStart, TaskItTime timeEnd, string category, Date date) {
	Task task(activity, venue, timeStart, timeEnd, category, date);
	Logger *_logFile;
	_logFile = Logger::getInstance();
	_listOfTasks.push_back(task);
	_numOfTask++;

	sortStorage();
	saveFile();
	_logFile->loggingEvents(LOG_STORAGE_ADD);
	
	int index = getTaskIndex(task);

	PreviousAction previousAction;
	previousAction.updateTask(ADD_TASK, task, index);
	_allPreviousActions.push(previousAction);

	return index;
}

void TaskItStorage::deleteTask(int numOfTask) {
	assert(numOfTask <= _numOfTask);
	Logger *_logFile;
	_logFile = Logger::getInstance();
	
	PreviousAction previousAction;
	previousAction.updateTask(DELETE_TASK, _listOfTasks[numOfTask - SHIFT_INDEX_BY_ONE], numOfTask - SHIFT_INDEX_BY_ONE);
	_allPreviousActions.push(previousAction);
	
	_listOfTasks.erase (_listOfTasks.begin() + (numOfTask - SHIFT_INDEX_BY_ONE) );
	_numOfTask--;

	sortStorage();
	saveFile();
	_logFile->loggingEvents(LOG_STORAGE_DELETED);	
	return;
}

int TaskItStorage::editTask(string activity, string venue, TaskItTime timeStart, TaskItTime timeEnd, string category, Date date, int numOfTask) {
	assert(numOfTask <= _numOfTask);
	Logger *_logFile;
	_logFile = Logger::getInstance();
	Task originalTask = _listOfTasks[numOfTask - SHIFT_INDEX_BY_ONE];
	size_t found;
	found = originalTask.getActivity().find(BLOCK_FUNCTION);

	if (found != string::npos) {
		activity = BLOCK_FUNCTION + activity;
	}
	_listOfTasks[numOfTask - SHIFT_INDEX_BY_ONE].editTask(activity, venue, timeStart, timeEnd, category, date);
	
	sortStorage();
	saveFile();

	Task editedTask(activity, venue, timeStart, timeEnd, category, date);
	if (originalTask.getDone()) {
		editedTask.markDone();
	}
	int index = getTaskIndex(editedTask);

	PreviousAction previousAction;
	previousAction.updateTask(EDIT_TASK, originalTask, index);
	_allPreviousActions.push(previousAction);

	_logFile->loggingEvents(LOG_STORAGE_EDIT);	
	return index;
}

string TaskItStorage::getTaskDetails(int index) {
	assert(index < _numOfTask);
	Logger *_logFile;
	_logFile = Logger::getInstance();
	_logFile->loggingEvents(LOG_STORAGE_RETRIEVE);	

	return _listOfTasks[index].getAllDetails();
}

void TaskItStorage::markTask(int numOfTask) {
	assert(numOfTask <= _numOfTask);
	Logger *_logFile;
	_logFile = Logger::getInstance();
	
	PreviousAction previousAction;
	previousAction.updateTask(MARK_TASK, _listOfTasks[numOfTask - SHIFT_INDEX_BY_ONE], numOfTask);
	_allPreviousActions.push(previousAction);

	_listOfTasks[numOfTask - SHIFT_INDEX_BY_ONE].markDone();
	saveFile();
	_logFile->loggingEvents(LOG_STORAGE_MAKE_DONE);
	return;
}

void TaskItStorage::unMarkTask(int numOfTask) {
	assert(numOfTask <= _numOfTask);
	Logger *_logFile;
	_logFile = Logger::getInstance();

	PreviousAction previousAction;
	previousAction.updateTask(UNMARK_TASK, _listOfTasks[numOfTask - SHIFT_INDEX_BY_ONE], numOfTask);
	_allPreviousActions.push(previousAction);

	_listOfTasks[numOfTask - SHIFT_INDEX_BY_ONE].markUndone();
	saveFile();
	_logFile->loggingEvents(LOG_STORAGE_MAKE_UNDONE);
	return;
}

void TaskItStorage::confirm(int numOfTask){
	assert(numOfTask <= _numOfTask);
	Task originalTask = _listOfTasks[numOfTask - SHIFT_INDEX_BY_ONE];
	_listOfTasks[numOfTask - SHIFT_INDEX_BY_ONE].confirm();
	Task newTask = _listOfTasks[numOfTask - SHIFT_INDEX_BY_ONE];
	
	PreviousAction previousAction;
	previousAction.clearVector();

	previousAction.addToPreviousConfirmTasks(originalTask);
	for (int i = INITIAL_VALUE; i < getNumOfTask(); i++){
		if (_listOfTasks[i].getActivity() == originalTask.getActivity()){
			previousAction.addToPreviousConfirmTasks(_listOfTasks[i]);
			_listOfTasks.erase(_listOfTasks.begin() + i);
			_numOfTask--;
			i--;
		}
	}
	previousAction.updateTask(CONFIRM, newTask, numOfTask - SHIFT_INDEX_BY_ONE);
	_allPreviousActions.push(previousAction);
	
	sortStorage();
	saveFile();
	return;
}

void TaskItStorage::undoTask() {
	Logger *_logFile;
	_logFile = Logger::getInstance();
	if (_allPreviousActions.empty()) {
		_logFile->loggingEvents(LOG_STORAGE_INVALID_UNDO);
		throw showToUser(INVALID_UNDO);
	} else {
		_previousAction = _allPreviousActions.top();
		_allPreviousActions.pop();

		CommandType previousCommand = _previousAction.getPreviousCommandForUndo();

		if (previousCommand == INVALID) {
			_logFile->loggingEvents(LOG_STORAGE_INVALID_UNDO);
			throw showToUser(INVALID_UNDO);
		} else {
			Task previousTask = _previousAction.getPreviousTask();
			int previousItemNumber = _previousAction.getPreviousItemNumber();

			if (previousCommand == ADD_TASK) {
				_listOfTasks.erase(_listOfTasks.begin() + previousItemNumber);
				_numOfTask--;
			} else if (previousCommand == DELETE_TASK) {
				_listOfTasks.insert(_listOfTasks.begin() + previousItemNumber, previousTask);
				_numOfTask++;
			} else if (previousCommand == EDIT_TASK) {
				_listOfTasks.erase(_listOfTasks.begin() + previousItemNumber);
				_listOfTasks.insert(_listOfTasks.begin() + previousItemNumber, previousTask);
			} else if (previousCommand == MARK_TASK) {
				_listOfTasks[previousItemNumber - SHIFT_INDEX_BY_ONE].markUndone();
			} else if (previousCommand == UNMARK_TASK) {
				_listOfTasks[previousItemNumber - SHIFT_INDEX_BY_ONE].markDone();
			} else if (previousCommand == CONFIRM){
				int index = getTaskIndex(previousTask);
				_listOfTasks.erase(_listOfTasks.begin() + index);
				
				vector<Task> previousConfirm = _previousAction.getPreviousConfirmTasks();
				int numOfPreviousConfirm = previousConfirm.size();
				_numOfTask = _numOfTask + numOfPreviousConfirm - SHIFT_INDEX_BY_ONE;
				
				for (int i = INITIAL_VALUE; i < numOfPreviousConfirm; i++) {
					_listOfTasks.push_back(previousConfirm[i]);
				}
			}
			sortStorage();
			saveFile();
			_logFile->loggingEvents(LOG_STORAGE_UNDO);
		}
	}
}

Task TaskItStorage::getTask(int index) {
	Logger *_logFile;
	_logFile = Logger::getInstance();

	if(index > getNumOfTask() - SHIFT_INDEX_BY_ONE) {
		_logFile->loggingEvents(LOG_STORAGE_INVALID_INDEX);
		throw INVALID_REQUEST;
	} else {
		_logFile->loggingEvents(LOG_STORAGE_RETRIEVE);
		return _listOfTasks[index];
	}
}

vector <int> TaskItStorage::getDatesWithTasks() {
	vector <int> datesWithTasks;
	
	for (int i = INITIAL_VALUE; i < getNumOfTask(); i++) {
		if (_listOfTasks[i].getDate().getHasDate()) {
			datesWithTasks.push_back(atoi(_listOfTasks[i].getDate().getDisplay().c_str()));
		}
	}

	return datesWithTasks;
}

int TaskItStorage::getNumOfFloatingTask() {
	int _numOfFloatingTask = 0;
	for (int i = INITIAL_VALUE; i < getNumOfTask(); i++) {
		if (_listOfTasks[i].getFloating()) {
			_numOfFloatingTask++;
		}
	}
	return _numOfFloatingTask;
}

int TaskItStorage::getNumOfTask() {
	return _numOfTask;
}

string TaskItStorage::showToUser(string info) {
	ostringstream displayInfo;
	Logger *_logFile;
	_logFile = Logger::getInstance();

	displayInfo << info << NEW_LINE;
	_logFile->loggingEvents(LOG_STORAGE_DISPLAY);
	return displayInfo.str();
}